﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.GeometriesGraph;
using NetTopologySuite.GeometriesGraph.Index;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Operation.Relate
{
	public class RelateComputer
	{
		public RelateComputer(GeometryGraph[] arg)
		{
			this._arg = arg;
		}

		public IntersectionMatrix ComputeIM()
		{
			IntersectionMatrix intersectionMatrix = new IntersectionMatrix();
			intersectionMatrix.Set(Location.Exterior, Location.Exterior, Dimension.Surface);
			if (!this._arg[0].Geometry.EnvelopeInternal.Intersects(this._arg[1].Geometry.EnvelopeInternal))
			{
				this.ComputeDisjointIM(intersectionMatrix);
				return intersectionMatrix;
			}
			this._arg[0].ComputeSelfNodes(this._li, false);
			this._arg[1].ComputeSelfNodes(this._li, false);
			SegmentIntersector intersector = this._arg[0].ComputeEdgeIntersections(this._arg[1], this._li, false);
			this.ComputeIntersectionNodes(0);
			this.ComputeIntersectionNodes(1);
			this.CopyNodesAndLabels(0);
			this.CopyNodesAndLabels(1);
			this.LabelIsolatedNodes();
			this.ComputeProperIntersectionIM(intersector, intersectionMatrix);
			EdgeEndBuilder edgeEndBuilder = new EdgeEndBuilder();
			IList<EdgeEnd> ee = edgeEndBuilder.ComputeEdgeEnds(this._arg[0].Edges);
			this.InsertEdgeEnds(ee);
			IList<EdgeEnd> ee2 = edgeEndBuilder.ComputeEdgeEnds(this._arg[1].Edges);
			this.InsertEdgeEnds(ee2);
			this.LabelNodeEdges();
			this.LabelIsolatedEdges(0, 1);
			this.LabelIsolatedEdges(1, 0);
			this.UpdateIM(intersectionMatrix);
			return intersectionMatrix;
		}

		private void InsertEdgeEnds(IEnumerable<EdgeEnd> ee)
		{
			foreach (EdgeEnd e in ee)
			{
				this._nodes.Add(e);
			}
		}

		private void ComputeProperIntersectionIM(SegmentIntersector intersector, IntersectionMatrix im)
		{
			Dimension dimension = this._arg[0].Geometry.Dimension;
			Dimension dimension2 = this._arg[1].Geometry.Dimension;
			bool hasProperIntersection = intersector.HasProperIntersection;
			bool hasProperInteriorIntersection = intersector.HasProperInteriorIntersection;
			if (dimension == Dimension.Surface && dimension2 == Dimension.Surface)
			{
				if (hasProperIntersection)
				{
					im.SetAtLeast("212101212");
					return;
				}
			}
			else if (dimension == Dimension.Surface && dimension2 == Dimension.Curve)
			{
				if (hasProperIntersection)
				{
					im.SetAtLeast("FFF0FFFF2");
				}
				if (hasProperInteriorIntersection)
				{
					im.SetAtLeast("1FFFFF1FF");
					return;
				}
			}
			else if (dimension == Dimension.Curve && dimension2 == Dimension.Surface)
			{
				if (hasProperIntersection)
				{
					im.SetAtLeast("F0FFFFFF2");
				}
				if (hasProperInteriorIntersection)
				{
					im.SetAtLeast("1F1FFFFFF");
					return;
				}
			}
			else if (dimension == Dimension.Curve && dimension2 == Dimension.Curve && hasProperInteriorIntersection)
			{
				im.SetAtLeast("0FFFFFFFF");
			}
		}

		private void CopyNodesAndLabels(int argIndex)
		{
			foreach (Node node in this._arg[argIndex].Nodes)
			{
				this._nodes.AddNode(node.Coordinate).SetLabel(argIndex, node.Label.GetLocation(argIndex));
			}
		}

		private void ComputeIntersectionNodes(int argIndex)
		{
			foreach (Edge edge in this._arg[argIndex].Edges)
			{
				Location location = edge.Label.GetLocation(argIndex);
				foreach (EdgeIntersection edgeIntersection in edge.EdgeIntersectionList)
				{
					RelateNode relateNode = (RelateNode)this._nodes.AddNode(edgeIntersection.Coordinate);
					if (location == Location.Boundary)
					{
						relateNode.SetLabelBoundary(argIndex);
					}
					else if (relateNode.Label.IsNull(argIndex))
					{
						relateNode.SetLabel(argIndex, Location.Interior);
					}
				}
			}
		}

		private void LabelIntersectionNodes(int argIndex)
		{
			foreach (Edge edge in this._arg[argIndex].Edges)
			{
				Location location = edge.Label.GetLocation(argIndex);
				foreach (EdgeIntersection edgeIntersection in edge.EdgeIntersectionList)
				{
					RelateNode relateNode = (RelateNode)this._nodes.Find(edgeIntersection.Coordinate);
					if (relateNode.Label.IsNull(argIndex))
					{
						if (location == Location.Boundary)
						{
							relateNode.SetLabelBoundary(argIndex);
						}
						else
						{
							relateNode.SetLabel(argIndex, Location.Interior);
						}
					}
				}
			}
		}

		private void ComputeDisjointIM(IntersectionMatrix im)
		{
			IGeometry geometry = this._arg[0].Geometry;
			if (!geometry.IsEmpty)
			{
				im.Set(Location.Interior, Location.Exterior, geometry.Dimension);
				im.Set(Location.Boundary, Location.Exterior, geometry.BoundaryDimension);
			}
			IGeometry geometry2 = this._arg[1].Geometry;
			if (!geometry2.IsEmpty)
			{
				im.Set(Location.Exterior, Location.Interior, geometry2.Dimension);
				im.Set(Location.Exterior, Location.Boundary, geometry2.BoundaryDimension);
			}
		}

		private void LabelNodeEdges()
		{
			foreach (Node node in this._nodes)
			{
				((RelateNode)node).Edges.ComputeLabelling(this._arg);
			}
		}

		private void UpdateIM(IntersectionMatrix im)
		{
			foreach (Edge edge in this._isolatedEdges)
			{
				edge.UpdateIM(im);
			}
			foreach (Node node in this._nodes)
			{
				RelateNode relateNode = (RelateNode)node;
				relateNode.UpdateIM(im);
				relateNode.UpdateIMFromEdges(im);
			}
		}

		private void LabelIsolatedEdges(int thisIndex, int targetIndex)
		{
			foreach (Edge edge in this._arg[thisIndex].Edges)
			{
				if (edge.IsIsolated)
				{
					this.LabelIsolatedEdge(edge, targetIndex, this._arg[targetIndex].Geometry);
					this._isolatedEdges.Add(edge);
				}
			}
		}

		private void LabelIsolatedEdge(Edge e, int targetIndex, IGeometry target)
		{
			if (target.Dimension > Dimension.Point)
			{
				Location location = this._ptLocator.Locate(e.Coordinate, target);
				e.Label.SetAllLocations(targetIndex, location);
				return;
			}
			e.Label.SetAllLocations(targetIndex, Location.Exterior);
		}

		private void LabelIsolatedNodes()
		{
			foreach (Node node in this._nodes)
			{
				Label label = node.Label;
				Assert.IsTrue(label.GeometryCount > 0, "node with empty label found");
				if (node.IsIsolated)
				{
					if (label.IsNull(0))
					{
						this.LabelIsolatedNode(node, 0);
					}
					else
					{
						this.LabelIsolatedNode(node, 1);
					}
				}
			}
		}

		private void LabelIsolatedNode(Node n, int targetIndex)
		{
			Location location = this._ptLocator.Locate(n.Coordinate, this._arg[targetIndex].Geometry);
			n.Label.SetAllLocations(targetIndex, location);
		}

		private readonly LineIntersector _li = new RobustLineIntersector();

		private readonly PointLocator _ptLocator = new PointLocator();

		private readonly GeometryGraph[] _arg;

		private readonly NodeMap _nodes = new NodeMap(new RelateNodeFactory());

		private readonly List<Edge> _isolatedEdges = new List<Edge>();
	}
}
